Skip to content

Conversation

@ryanolee
Copy link

@ryanolee ryanolee commented Jun 19, 2025

Question Answer
JIRA issue IBX-10186
Type improvement
Target Ibexa version v3.3
BC breaks no
Depends on #412 (Included with this PR)
Required by ezsystems/ezplatform-admin-ui#2125

Warning

This PR contains all of the changes from #412 verbatim. Please read the description of that PR for relevant details of changes from that branch.

What

This PR continues the work started in IBX-10174 by adding a $limit clause to LocationService::count and ContentService::count count. The Public facing API's have been changed accordingly.
The changes to the public API this PR specifically makes are:

interface LocationService {
    # ...

    public function count(Filter $filter, ?array $languages = null): int;

    # ...
}

interface ContentService {
    # ...

     count(Filter $filter, ?array $languages = null): int;

    # ...
}

To the following:

interface LocationService {
    # ...

    public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int;

    # ...
}

interface ContentService {
    # ...

     count(Filter $filter, ?array $languages = null, ?int $filter = null): int;

    # ...
}

This opens up the ability to limit the total item count pulled back from the DB and proportionally cuts down on overall processing times.
This is especially useful for when:

  • You want to ascertain if there is any sub items / a single instance of an item matching a count (A very common case as seen in the associated admin PR)
  • If you want to know if there is a count up to a certain number, which is a useful mechanism for regulating the impact of otherwise potentially very expensive queries

How

Based on the above API changes most top level repository count queries have a limit option. A new trait has been added for converting SQL Like:

SELECT DISTINCT COUNT(DISTINCT location.node_id) FROM ezcontentobject_tree location INNER JOIN ezcontentobject content ON content.id = location.contentobject_id INNER JOIN ezcontentobject_version version ON (content.id = version.contentobject_id) AND (content.current_version = version.version) AND (version.status = 1) WHERE location.parent_node_id IN (43) 

to

SELECT COUNT(*) FROM( SELECT DISTINCT location.node_id FROM ezcontentobject_tree location INNER JOIN ezcontentobject content ON content.id = location.contentobject_id INNER JOIN ezcontentobject_version version ON (content.id = version.contentobject_id) AND (content.current_version = version.version) AND (version.status = 1) WHERE location.parent_node_id IN (43) LIMIT {x} ) AS x;

It should be noted that the count query does not change until a limit is directly passed. Given by default this is set to null no underlying queries made by current sites will change unless a limit is passed meaning this PR should result in no BC breaks

Why

Performance of eZ Platform Count queries (Given the number of required joins and existing database schema) scale poorly, especially in cases where many content items match the given count query. This is exacerbated when the associated user has a larger set of permissions

From benchmarking here is how eZ Scales to a location count query with no limitations .
query_performance_admin_2

As you can see the number of items pulled back is directly proportional the time the query takes to resolve. (The red dot representing the current implementation). In the CMS many such queries are made to generate pages over the lifespan of the request. Many of which can be satisfied with limits of 1 or a lower number.

For our use case the effect of these queries are so catastrophic the CMS is completely unusable / some pages take 10+ seconds to load. (Most of this can be attributed to IBX-10186 and IBX-10174)

This trend gets even worse with the eZ Permissions system where Count queries can quickly spiral into the single limiting factor in overall page performance. (See below a chat like above be the query is bounded by some more specific permissions)
query_performance

Checklist:

  • Provided PR description.
  • Tested the solution manually.
  • Provided automated test coverage.
  • Checked that target branch is set correctly (master for features, the oldest supported for bugs).
  • Ran PHP CS Fixer for new PHP code (use $ composer fix-cs).
  • Asked for a review (ping @ezsystems/engineering-team).

@sonarqubecloud
Copy link

@ryanolee
Copy link
Author

Closed WRT ibexa/core#600 (review)

@ryanolee ryanolee closed this Jun 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants